// Drawing functions with no-clipping needed
//

#include <stdlib.h>
#include <string.h>
#include <math.h>           // for sqrt()
#include "Common/Debug.h"
#include "Common/Macros.h"
#include "DrawShared.h"
#include "DrawNC.h"
#include "Draw.h"

/////////////////////////////////////////////////////////////////////////////
// No clipping (boundary checking) drawing

void g_DrawPixelNC(const TCanvas *canvas, int x, int y, int color)
{
#ifdef DIRECT_BUFFER_ACCESS
    UInt8 *ptr = FRAME_ANCHOR(x, y);
	WRITE_PIXEL_PTR(ptr, color);
#endif
}

void g_DrawHLineNCBlend(const TCanvas *canvas, int x1, int y1, int x2, int color, int src, int des)
{
#ifdef DIRECT_BUFFER_ACCESS
    UInt8 *ptr;
#endif

    if (x1 > x2)
        SWAP(x1, x2);

#ifdef DIRECT_BUFFER_ACCESS
    ptr = FRAME_ANCHOR(x1, y1);
#endif

    x2 = x2 - x1 + 1;

#ifdef DIRECT_BUFFER_ACCESS
#ifdef __RGB16
    color |= (color << 16);         // fill two 16 bits color
#endif
   // memset_color(ptr, color, x2);
	memset_blend( ptr, color, x2, src, des );
#else        
    while (x2--)
    {
        DRAW_PIXEL_NC(x1, y1, color);
        x1++;
    }
#endif
}

void g_DrawHLineNC(const TCanvas *canvas, int x1, int y1, int x2, int color)
{
#ifdef DIRECT_BUFFER_ACCESS
    UInt8 *ptr;
#endif

    if (x1 > x2)
        SWAP(x1, x2);

#ifdef DIRECT_BUFFER_ACCESS
    ptr = FRAME_ANCHOR(x1, y1);
#endif

    x2 = x2 - x1 + 1;

#ifdef DIRECT_BUFFER_ACCESS
#ifdef __RGB16
    color |= (color << 16);         // fill two 16 bits color
#endif
    memset_color(ptr, color, x2);
#else        
    while (x2--)
    {
        DRAW_PIXEL_NC(x1, y1, color);
        x1++;
    }
#endif
}

void g_DrawVLineNC(const TCanvas *canvas, int x1, int y1, int y2, int color)
{
#ifdef DIRECT_BUFFER_ACCESS
    UInt8 *ptr;
#endif

    if (y1 > y2)
        SWAP(y1, y2);

#ifdef DIRECT_BUFFER_ACCESS
    ptr = FRAME_ANCHOR(x1, y1);
#endif

    y2 = y2 - y1 + 1;

    while (y2--)
    {
#ifdef DIRECT_BUFFER_ACCESS
		WRITE_PIXEL_PTR(ptr, color);
        ptr += FRAME_STRIDE;
#else
        DRAW_PIXEL_NC(x1, y1, color);
        y1++;
#endif            
    }
}

/*  Draw arbitrary line
 *
 *  Using Bresenham's algorithm.
 *  http://www.cs.helsinki.fi/group/goa/mallinnus/lines/bresenh.html
 */
void g_DrawLineNC(const TCanvas *canvas, int x1, int y1, int x2, int y2, int color)
{
    int dx, dy, row;
    int d, incr1, incr2;

#ifdef DIRECT_BUFFER_ACCESS
    UInt8 *ptr;
#endif

    if (x1 > x2)
    {
        SWAP(x1, x2);
        SWAP(y1, y2);
    }

#ifdef DIRECT_BUFFER_ACCESS
    ptr = FRAME_ANCHOR(x1, y1);
    row = (y2 > y1) ? FRAME_STRIDE : (-FRAME_STRIDE);
#else
    row = (y2 > y1) ? 1 : (-1);
#endif

    dx = x2 - x1;
    dy = ABS_SUB(y2, y1);

    // Draw vertical line only...
    if (!dx)
    {
        dy++;
        while (dy--)
        {
#ifdef DIRECT_BUFFER_ACCESS
			WRITE_PIXEL_PTR(ptr, color);
            ptr += row;
#else
            DRAW_PIXEL_NC(x1, y1, color);
            y1 += row;
#endif            
        }
        return;
    }

    // Draw horizontal line only...
    if (!dy)
    {
        dx++;
#ifdef DIRECT_BUFFER_ACCESS
#ifdef __RGB16
        color |= (color << 16);         // fill two 16 bits color
#endif
        memset_color(ptr, color, dx);
#else        
        while (dx--)
        {
            DRAW_PIXEL_NC(x1, y1, color);
            x1++;
        }
#endif
        return;
    }

    // Draw sloping line only...
    // If more-or-less horizontal
    if (dx >= dy)
    {
#ifdef DIRECT_BUFFER_ACCESS
		WRITE_PIXEL_PTR(ptr, color);
        ptr += PIXEL_SIZE;
#else
        DRAW_PIXEL_NC(x1, y1, color);
        x1++;
#endif
        d     = (dy << 1) - dx;
        incr1 = (dy - dx) << 1;
        incr2 = dy << 1;

        while (dx--)
        {
            if (d > 0)
            {
#ifdef DIRECT_BUFFER_ACCESS
                ptr += row;
#else
                y1 += row;
#endif
                d += incr1;
            }
            else
                d += incr2;
			
#ifdef DIRECT_BUFFER_ACCESS
			WRITE_PIXEL_PTR(ptr, color);	
            ptr += PIXEL_SIZE;
#else
            DRAW_PIXEL_NC(x1, y1, color);
            x1++;      
#endif
        }
    }
    else // more-or-less vertical
    {
#ifdef DIRECT_BUFFER_ACCESS
		WRITE_PIXEL_PTR(ptr, color);
        ptr += row;
#else
        DRAW_PIXEL_NC(x1, y1, color);
        y1 += row;         
#endif
        d     = (dx << 1) - dy;
        incr1 = (dx - dy) << 1;
        incr2 = dx << 1;

        while (dy--)
        {
            if (d > 0)
            {
#ifdef DIRECT_BUFFER_ACCESS
                ptr += PIXEL_SIZE;
#else
                x1++;
#endif
                d += incr1;
            }
            else
                d += incr2;

#ifdef DIRECT_BUFFER_ACCESS
			WRITE_PIXEL_PTR(ptr, color);
            ptr += row;
#else
			DRAW_PIXEL_NC(x1, y1, color);
			y1 += row; 
#endif
        }
    }
}

void g_DrawRectNC(const TCanvas *canvas, int l, int t, int r, int b, int color)
{
    g_DrawHLineNC(canvas, l, t, r, color);
    g_DrawHLineNC(canvas, l, b, r, color);
	g_DrawVLineNC(canvas, l, t+1, b-1, color);
    g_DrawVLineNC(canvas, r, t+1, b-1, color);
}

void g_DrawCircleNC(const TCanvas *canvas, int x, int y, int radius, int color)
{
	int axis1, axis2;
	FLOAT rxr = (FLOAT) radius * radius;
    
	axis1 = 0;
	axis2 = radius;

	do
	{
		g_DrawPixelNC(canvas, x + axis1, y - axis2, color);
		g_DrawPixelNC(canvas, x - axis1, y - axis2, color);
		g_DrawPixelNC(canvas, x + axis1, y + axis2, color);
		g_DrawPixelNC(canvas, x - axis1, y + axis2, color);
		g_DrawPixelNC(canvas, x + axis2, y - axis1, color);
		g_DrawPixelNC(canvas, x - axis2, y - axis1, color);
		g_DrawPixelNC(canvas, x + axis2, y + axis1, color);
		g_DrawPixelNC(canvas, x - axis2, y + axis1, color);

		axis1++;
		axis2 = (int) (sqrt(rxr - ((FLOAT) axis1 * axis1)) + 0.5);

    } while (axis1 <= axis2);
}

void g_DrawImageNC(const TCanvas *canvas, int x, int y, const TImage *image)
{
    int row, height;
    const UInt8 *data;

#ifdef DIRECT_BUFFER_ACCESS
    UInt8 *ptr;

    row = PIXEL_TO_BYTE(image->width);
    height = image->height;
    data = (UInt8*)image->data;

    ptr = FRAME_ANCHOR(x, y);

    while (height--)
    {
        memcpy(ptr, data, row);
        data += row;
        ptr += FRAME_STRIDE;     
    }
#else
    int r = x + image->width - 1;
    int b = y + image->height - 1;

    data = image->data;

    for (y < b; y++)
    {
        for (x < r; x++)
        {   
            DRAW_PIXEL_NC(x, y, READ_PIXEL_PTR(data));
            data += PIXEL_SIZE;      
        }
    }
#endif
}

void g_DrawImageTransNC(const TCanvas *canvas, int x, int y, const TImage *image, int keyColor)
{
#ifdef DIRECT_BUFFER_ACCESS
    int row, height, c;
    const UInt8 *data;
    UInt8 *ptr;

    row = PIXEL_TO_BYTE(image->width);
    height = image->height;
    data = (UInt8*)image->data;

    ptr = FRAME_ANCHOR(x, y);

    while (height--)
    {
        for (x = 0; x < row; x += PIXEL_SIZE)
        {
            c = READ_PIXEL_PTR(data + x);

            if (c - keyColor)
				WRITE_PIXEL_PTR(ptr + x, c);
        }
        data += row;
        ptr += FRAME_STRIDE;     
    }
#else
    int row, height, c;
    const UInt8 *data;

    r = x + image->width - 1;
    b = y + image->height - 1;
    data = image->data;

    for (y < b; y++)
    {
        for (x < r; x++)
        {   
			c = READ_PIXEL_PTR(data)

			if (c - keyColor)
	            DRAW_PIXEL_NC(x, y, c);

            data += PIXEL_SIZE; 
        }
    }
#endif
}

void g_FillRectNC(const TCanvas *canvas, int l, int t, int r, int b, int color)
{
	g_FillRect( canvas, l, t, r, b, color );
/*    int width, height;

#ifdef DIRECT_BUFFER_ACCESS
    UInt8 *ptr;
	
	ptr = FRAME_ANCHOR(l, t);
#endif

    width  = r - l + 1;
    height = b - t + 1;

#ifdef DIRECT_BUFFER_ACCESS
#ifdef __RGB16
    color |= (color << 16);
#endif
#endif

    while (height--)
    {
#ifdef DIRECT_BUFFER_ACCESS
        memset_color(ptr, color, width);
        ptr += FRAME_STRIDE;
#else
        g_DrawHLineNC(canvas, l, t, r, color);
        t++;
#endif
    }*/
}

void g_FillCircleNC(const TCanvas *canvas, int x, int y, int radius, int color)
{
    int line, dx;
    FLOAT rxr;

    rxr = (FLOAT) radius * radius;

    for (line = y - radius; line <= y + radius; line++)
    {
        dx = (int) (sqrt(rxr - ((FLOAT)((y-line)*(y-line)))) + 0.5);

		g_DrawHLineNC(canvas,  x - dx, line, x + dx, color);
    }
}


/////////////////////////////////////////////////////////////////////////////
// Alpha blending functions (no-clipping version)

// NOTE: only available in DIRECT_BUFFER_ACCESS enable
#ifdef DIRECT_BUFFER_ACCESS

void g_DrawImageDarkenNC(const TCanvas *canvas, int x, int y, const TImage *image)
{
    int row, height;
    const UInt8 *data;
    UInt8 *ptr;

    row = PIXEL_TO_BYTE(image->width);
    height = image->height;
    data = (UInt8*)image->data;

    ptr = FRAME_ANCHOR(x, y);

    while (height--)
    {
        memcpy_darken(ptr, data, image->width);
        data += row;
        ptr += FRAME_STRIDE;     
    }    
}

void g_FillRectDarkenNC(const TCanvas *canvas, int l, int t, int r, int b, int color)
{
    int width, height;
    UInt8 *ptr;
    
    ptr = FRAME_ANCHOR(l, t);

    width  = r - l + 1;
    height = b - t + 1;

#ifdef __RGB16
    color |= (color << 16);
#endif

    while (height--)
    {
        memset_darken(ptr, color, width);
        ptr += FRAME_STRIDE;
    }
}

void g_DrawImageBlendNC(const TCanvas *canvas, int x, int y, const TImage *image, int sf, int df)
{
    int row, height;
    const UInt8 *data;
    UInt8 *ptr;

    row = PIXEL_TO_BYTE(image->width);
    height = image->height;
    data = (UInt8*)image->data;

    ptr = FRAME_ANCHOR(x, y);

    while (height--)
    {
        memcpy_blend(ptr, data, image->width, sf, df);
        data += row;
        ptr += FRAME_STRIDE;     
    }   
}

void g_FillRectBlendNC(const TCanvas *canvas, int l, int t, int r, int b, int color, int sf, int df)
{
    int width, height;
    UInt8 *ptr;
   
    ptr = FRAME_ANCHOR(l, t);

    width  = r - l + 1;
    height = b - t + 1;

    while (height--)
    {
        memset_blend(ptr, color, width, sf, df);
        ptr += FRAME_STRIDE;
    }
}

void g_DrawImageAlphaNC(const TCanvas *canvas, int x, int y, const TImage *image, const UInt8 *alpha)
{
    int row, height;
    const UInt8 *data, *afa;
    UInt8 *ptr;

    row = PIXEL_TO_BYTE(image->width);
    height = image->height;
    data = (UInt8*)image->data;
	afa = alpha;

    ptr = FRAME_ANCHOR(x, y);

    while (height--)
    {
        memcpy_alpha_array(ptr, data, image->width, afa);
        data += row;
		afa += image->width;
        ptr += FRAME_STRIDE;     
    } 
}

void g_DrawPixelAlphaNC(const TCanvas* canvas, int x, int y, int color, UInt8 alpha)
{
    UInt8 *ptr;

	ptr = FRAME_ANCHOR(x, y);

	write_pixel_alpha(ptr, color, alpha);
}

int  IntensityBits			 =8;
int  NumLevels				 =256;	//= 2^IntensityBits
int  WeightingComplementMask =255;	//= (NumLevels-1)
int  IntensityShift			 =8;	//= (16 - IntensityBits)

void g_SetLineAABitsLevel(int bits, int level)
{
	WeightingComplementMask = (level-1);
	IntensityShift = (16 - bits);
}

// NOTE: lines typically no more than 1K long; for longer lines, 32-bit error
//		 arithmetic must be used to avoid problems with fixed-point inaccuracy.
void g_DrawLineAANC(const TCanvas* canvas, int x1, int y1, int x2, int y2, int color)
{
    UInt8 *ptr;
    int dx, dy, row;
    UInt16 error, errorAcc = 0, errorAdj;
    UInt8 weight;

    if(x1 > x2)
	{
		SWAP(y1, y2);
		SWAP(x1, x2);
    }

	// Draw first pixel!
	ptr = FRAME_ANCHOR(x1, y1);		

//	WRITE_PIXEL_PTR(ptr, color);
	write_pixel_alpha(ptr, color, (UInt8) WeightingComplementMask);

    dx = x2 - x1;
    dy = y2 - y1;

    if (dy >= 0)
		row = FRAME_STRIDE;
    else
	{
		dy = -dy;
		row = -FRAME_STRIDE;
    }

    // Draw vertical line only...
    if (!dx)
    {
		// Write remains other than first pixel
        while (dy--)
        {
			ptr += row;

//			WRITE_PIXEL_PTR(ptr, color);   
			write_pixel_alpha(ptr, color, (UInt8) WeightingComplementMask);
        }
        return;
    }

    // Draw horizontal line only...
    if (!dy)
    {
//#ifdef __RGB16
//        color |= (color << 16);		// fill two 16 bits color
//#endif
		// Write remains other than first pixel
//      memset_color(ptr + PIXEL_SIZE, color, dx);
		memset_alpha(ptr + PIXEL_SIZE, color, dx, (UInt8) WeightingComplementMask);
        return;
    }	

    // Draw diagonal line only...
    if (dx == dy)
	{
		ptr += PIXEL_SIZE;
		ptr += row;

		while (dx--)
		{
//			WRITE_PIXEL_PTR(ptr, color);		//1. don't want anti-aliased in 45 deg line
			write_pixel_alpha(ptr, color, 100);	//2. or want it!

			ptr += PIXEL_SIZE;
			write_pixel_alpha(ptr, color, 200);	//2. or want it!

			ptr += row;
		}

		return;
    }

	// Draw other sloping line only...(using Wu Anti-Aliasing)
    if( dy > dx	)
	{ 
		// Y-major line
		errorAdj = (UInt16)(((UInt32) dx << 16) / (UInt32) dy);

		// Draw all pixels other than the first and last
		while (--dy)
		{
			error = errorAcc;		// remember current accumulated error

			errorAcc += errorAdj;	// calculate error for next pixel

			if (errorAcc <= error)	// if turned over
				ptr += PIXEL_SIZE;	// advance X
			
			ptr += row;				// always advance Y

			weight = errorAcc >> IntensityShift;

			write_pixel_alpha(ptr, color, (UInt8)(weight ^ WeightingComplementMask) );
			write_pixel_alpha(ptr + PIXEL_SIZE, color, weight);
		}
    }
	else
	{ 
		// X-major line
		errorAdj = (UInt16)(((UInt32) dy << 16) / (UInt32) dx);

		// Draw all pixels other than the first and last
		while (--dx)
		{
			error = errorAcc;		// remember current accumulated error

			errorAcc += errorAdj;	// calculate error for next pixel

			if (errorAcc <= error)	// if turned over
				ptr += row;			// advance Y
			
			ptr += PIXEL_SIZE;		// always advance X
			
			weight = errorAcc >> IntensityShift;

			write_pixel_alpha(ptr, color, (UInt8)(weight ^ WeightingComplementMask));
			write_pixel_alpha(ptr + row, color, weight);
		}
	}

	// Last pixel always get set too!
	ptr = FRAME_ANCHOR(x2, y2);	
	
//	WRITE_PIXEL_PTR(ptr, color);
	write_pixel_alpha(ptr, color, (UInt8) NumLevels);
}

#endif	// DIRECT_BUFFER_ACCESS
